package com.itguigu.process.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itguigu.auth.service.SysUserService;
import com.itguigu.model.process.Process;
import com.itguigu.model.process.ProcessRecord;
import com.itguigu.model.process.ProcessTemplate;
import com.itguigu.model.system.SysUser;
import com.itguigu.process.mapper.ProcessMapper;
import com.itguigu.process.service.ProcessRecordService;
import com.itguigu.process.service.ProcessService;
import com.itguigu.process.service.ProcessTemplateService;
import com.itguigu.security.custom.LoginUserInfoHelper;
import com.itguigu.vo.process.ApprovalVo;
import com.itguigu.vo.process.ProcessFormVo;
import com.itguigu.vo.process.ProcessQueryVo;
import com.itguigu.vo.process.ProcessVo;
import com.itguigu.wechat.service.MessageService;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipInputStream;


/**
 *
 */
@Service
public class ProcessServiceImpl extends ServiceImpl<ProcessMapper, Process> implements ProcessService {

    @Resource
    private MessageService messageService;

    @Autowired
    private ProcessMapper processMapper;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private ProcessTemplateService processTemplateService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private ProcessRecordService processRecordService;

    @Resource
    private HistoryService historyService;

    @Override
    public IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo) {
        IPage<ProcessVo> page = processMapper.selectPage(pageParam, processQueryVo);
        return page;
    }

    @Override
    public void deployByZip(String deployPath) {
        // 定义zip输入流
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(deployPath);
        ZipInputStream zis = new ZipInputStream(is);
        // 流程部署
        Deployment deploy = repositoryService
                .createDeployment()
                .addZipInputStream(zis)
                .deploy();
    }

    @Override
    public void startUp(ProcessFormVo processFormVo) {
        // 根据 用户id 获取 用户信息
        SysUser sysUser = sysUserService.getById(LoginUserInfoHelper.getUserId());

        // 根据模板id  获取 模板信息查询
        ProcessTemplate processTemplate = processTemplateService.getById(processFormVo.getProcessTemplateId());

        // 创建 process流程对象 , 并添加
        Process process = new Process();
        // 数据替换 : 将vo 的数据 拷贝到对应的process中
        BeanUtils.copyProperties(processFormVo, process);
        process.setProcessCode(System.currentTimeMillis() + ""); // 审批code
        process.setUserId(LoginUserInfoHelper.getUserId()); // 用户id
        process.setTitle(sysUser.getName() + "发起" + processTemplate.getName() + "申请");  // 标题
        process.setStatus(1); // 状态1 : 审批中  状态2 : 通过  -1 : 驳回
        process.setCreateTime(new Date());
        baseMapper.insert(process);

        // 启动流程 -- 三个参数
        /**
         * ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables);
         * processDefinitionKey :流程定义key   processTemplate,getProcessDefinitionKey();
         * businessKey :  业务key :   processId
         * variables :  流程参数  :   form表单json数据  转换成 map集合
         */
        String processDefinitionKey = processTemplate.getProcessDefinitionKey();
        Long businessKey = process.getId();
        // 流程参数
        HashMap<String, Object> variables = new HashMap<>();
        // 解析json 装成 map集合
        // json字符串 转 json对象
        JSONObject jsonObject = JSON.parseObject(process.getFormValues());
        // json对象 中的 关于 formData的内容
        JSONObject formData = jsonObject.getJSONObject("formData");
        HashMap<String, Object> map = new HashMap<>();
        // 将值传入
        for (Map.Entry<String, Object> entry : formData.entrySet()) {
            map.put(entry.getKey(), entry.getValue());
        }
        // 将值放入 流程参数
        variables.put("data", map);
        // 启动流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, String.valueOf(businessKey), variables);


        // 业务表 关联 当前流程实例id
        String processInstanceId = processInstance.getId();
        process.setProcessInstanceId(processInstanceId);

        // 计算找到下一个审批人 , 可能存在多个(并行审批)  --->  通过流程实例id进行查询
        List<Task> taskList = this.getCurrentTaskList(processInstanceId);
        // 定义审批人 真是姓名集合
        ArrayList<String> assigneeList = new ArrayList<>();
        // 判断是否为空
        if (!CollectionUtils.isEmpty(taskList)) {
            for (Task task : taskList) {
                // 获取到审批人   ---> 登录名称
                String assignee = task.getAssignee();
                // 根据审批人名称 得到 审批人用户是谁   --->  根据登录名称  查询用户信息  --->  得到真实姓名
                SysUser user = sysUserService.getUserByUserName(assignee);
                assigneeList.add(user.getName());

                // TODO 6 推送消息  --> 使用微信公众号实现
                messageService.pushPendingMessage(process.getId(), sysUser.getId(),task.getId());
            }
        }
        // 业务和 流程管理 , 更新 oa_process数据  ---> 等待  张三 , 李四  ,  王五  审批
        process.setDescription("等待" + StringUtils.join(assigneeList.toArray(), ",") + "审批");
        // 更新
        processMapper.updateById(process);

        // 提交 记录操作审批信息记录
        processRecordService.record(process.getId(), 1, "发起申请");
    }

    /**
     * 审核人 查询 待处理列表
     *
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findPending(Page<Process> pageParam) {
        // 根据当前用户的 审批人名称 即登录名称  查询任务
        String username = LoginUserInfoHelper.getUsername();
        // TODO  上一级没有正切提交吗?
        TaskQuery query = getTaskByUserName(username);

        // 获取任务集合 对应页的结果值  Executes the query and get a list of entities as the result.
        List<Task> taskList = query.listPage((int) ((pageParam.getCurrent() - 1) * pageParam.getSize()), (int) pageParam.getSize());
        // 获取 待审批的 个数
        long totalCount = query.count();

        // 封装 返回list集合数据 到 List<ProcessVo>里面
        // List<Task>  --->  List<ProcessVo>
        // 定义一个最终的集合
        List<ProcessVo> processVoList = new ArrayList<>();
        for (Task task : taskList) {
            // 获取任务的 流程实例id
            String processInstanceId = task.getProcessInstanceId();
            // 根据 流程实例id 得到流程实例
            ProcessInstance processInstance = runtimeService
                    .createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            // 判断流程实例是否 为空
            if (processInstance == null) {
                // 没有 就跳过 进行下一个
                continue;
            }
            // 拿到流程实例的 业务key
            String businessKey = processInstance.getBusinessKey();
            // 判断 业务key 是否为空
            if (businessKey == null) {
                // 没有 页跳过 , 进行下一个
                continue;
            }
            // 通过 业务key  , 获取到 该流程信息
            Process process = this.getById(Long.parseLong(businessKey));
            // 将 process copy 到 processVo当中
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            // 另外的赋值 --> 任务id
            processVo.setTaskId(task.getId());
            // 将该 任务放入到 结果集合当中去
            processVoList.add(processVo);
        }
        // 封装Ipage对象
        IPage<ProcessVo> page = new Page<>(pageParam.getCurrent(), pageParam.getSize(), totalCount);
        page.setRecords(processVoList);
        return page;
    }


    /**
     * 查询展示 审批详情 , 通过审批id
     *
     * @param id
     * @return
     */
    @Override
    public Map<String, Object> showPendingInfo(Long id) {
        // 通过 审批id 获取 该流程对象信息
        Process process = this.getById(id);
        //  查询对应 的流程提交记录 , processId = id
        List<ProcessRecord> processRecordList = processRecordService.list(new LambdaQueryWrapper<ProcessRecord>().eq(ProcessRecord::getProcessId, id));
        // 根据 流程对象中的 流程模板id 查询流程模板对象 信息
        ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateId());

        // 计算当前用户是否可以审批 , 能够查看详情的用户不是都能审批的 , 审批后也不能重复审批
        boolean isApprove = false;
        // 根据 流程对象中的流程实例id 获取该用户当前任务列表
        List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
        // 判断是否为空
        // 924a37bd-3f9c-11ef-9e42-9078412ee221
        if (!CollectionUtils.isEmpty(taskList)) {
            // 不为空
            // 遍历该任务列表 , 找到 任务 审批人 是自己的时候 , true
            for (Task task : taskList) {
                if (task.getAssignee().equals(LoginUserInfoHelper.getUsername())) {
                    // 该任务是我当前是我审批的
                    isApprove = true;
                }
            }
        }
        // 将上述信息存入返回结果的 map中
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("process", process);
        resultMap.put("processRecordList", processRecordList);
        resultMap.put("processTemplate", processTemplate);
        resultMap.put("isApprove", isApprove);
        return resultMap;
    }

    /**
     * 审批流程  通过 | 不通过
     *
     * @param approvalVo
     */
    @Override
    public void approve(ApprovalVo approvalVo) {
        // 根据任务id , 获取任务流程变量信息集合
        String taskId = approvalVo.getTaskId();
        Map<String, Object> variables = taskService.getVariables(taskId);
        variables.forEach((k, v) -> {
            System.out.println("流程变量variables : " + k + " = " + v);
        });
        // 根据 传过来的 status 状态 1 | -1  确定审批 通过 还是不通过
        Integer status = approvalVo.getStatus();
        if (status == 1) {
            // 审批通过  , 提交完成任务  ---> 正常情况 , 但是没有写 variables
            // taskService.complete(taskId, variables);
            taskService.complete(taskId);
        } else {
            // 审批驳回 --->  直接结束任务
            this.endTask(taskId);
        }

        // 根据状态值 , 编写 描述信息
        String description = approvalVo.getStatus().intValue() == 1 ? "已通过" : "驳回";
        // 添加 流程记录信息
        processRecordService.record(approvalVo.getProcessId(), approvalVo.getStatus(), description);

        // 计算下一个审批人
        // 通过流程id 获取 流程对象
        Process process = baseMapper.selectById(approvalVo.getProcessId());
        // 根据流程对象中的  流程实例对象 去获取 任务列表
        List<Task> currentTaskList = this.getCurrentTaskList(process.getProcessInstanceId());
        // 判断任务列表是否为空
        List<String> assigneeList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(currentTaskList)) {
            // 如果不为空
            // 遍历任务 , 通过任务审批人(登录用户名)  -->  查询对应用户信息  --->  将审批人真实姓名 放入集合中
            for (Task task : currentTaskList) {
                String assignee = task.getAssignee();
                SysUser sysUser = sysUserService.getUserByUserName(assignee);
                assigneeList.add(sysUser.getName());

                // TODO : 审批流程 -- 推送消息给下一个审批人  -- 公众号消息推送实现
                messageService.pushPendingMessage(process.getId(), sysUser.getId(), task.getId());
            }
            // 更新 流程描述  | 状态
            process.setDescription("等待" + StringUtils.join(assigneeList.toArray(), ",") + "审批");
            process.setStatus(1);
        } else {
            // 如果为空  --->  到头了 , 判断状态码 并修改状态
            if (approvalVo.getStatus().intValue() == 1) {
                process.setDescription("审批完成(同意)");
                process.setStatus(2);
            } else {
                // 审批完成 ,但是拒绝
                process.setDescription("审批完成(拒绝)");
                process.setStatus(-1);
            }
        }
        // 推送消息给申请人
        // 更新流程对象
        this.updateById(process);
    }

    /**
     * 查询 审批 已处理功能
     *
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
        // 封装查询条件
        // 获取 当前用户名
        String username = LoginUserInfoHelper.getUsername();
        // 根据当前用户名 擦汗寻历史任务实例
        HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(username)
                .finished()   // Only select historic task instances which are finished.
                .orderByTaskCreateTime()
                .desc();

        // 调用方法条件分页查询 , 返回list集合
        // 获取指定 页得数据集合  --- 开始位置 = (当前页 -1 ) × 每页个数  ,  每页记录数
        List<HistoricTaskInstance> historicTaskList = taskInstanceQuery.listPage(
                (int) ((pageParam.getCurrent() - 1) * pageParam.getSize()),
                (int) pageParam.getSize()
        );

        // 遍历 历史任务集合  , 封装list<ProcessVo>
        List<ProcessVo> processVoList = new ArrayList<>();
        for (HistoricTaskInstance historicTask : historicTaskList) {
            // 根据 该任务得流程实例id  去流程中 查询对应得 流程对象
            String processInstanceId = historicTask.getProcessInstanceId();
            Process process = baseMapper.selectOne(new LambdaQueryWrapper<Process>().eq(Process::getProcessInstanceId, processInstanceId));
            // 将 查询到的流程对象  封装到 vo 中
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            processVo.setTaskId("0");
            processVoList.add(processVo);
        }

        // 将 集合 分装到 Ipage中
        IPage<ProcessVo> page = new Page<>(pageParam.getCurrent(), pageParam.getSize(), taskInstanceQuery.count());
        page.setRecords(processVoList);
        return page;
    }

    /**
     * 已发起功能实现 , 谁提交申请, 谁就是已发起 , 从oa-process表中进行查询
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
        // 定义process查询vo
        ProcessQueryVo processQueryVo = new ProcessQueryVo();
        // 设置用户id
        processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
        IPage<ProcessVo> page = processMapper.selectPage(pageParam, processQueryVo);
        for (ProcessVo process : page.getRecords()) {
            process.setTaskId("0");
        }
        return page;
    }

    /**
     * 审批驳回 功能
     *
     * @param taskId : 任务id
     */
    private void endTask(String taskId) {
        // 1. 根据任务id 获取任务对象 task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 2. 获取流程定义模型 BpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        // 3. 获取 结束流向节点
        // 结束流向集合
        List endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        if (CollectionUtils.isEmpty(endEventList)) {
            // 结束流向节点集合为空  , 直接停止
            return;
        }
        // 获取结束流向节点
        FlowNode endFlowNode = (FlowNode) endEventList.get(0);

        // 4. 获取 当前流向节点  --> 根据任务对象中的 任务默认key
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());

        // 5. 清理当前流动方向
        // 先保存当前活动的原始方向
        List originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        // 然后清理当前流动方向
        currentFlowNode.getOutgoingFlows().clear();

        // 6. 创建新流向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        // 新流向id
        newSequenceFlow.setId("newSequenceFlowId");
        // 新流向当前资源节点
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        // 新流向目标流向节点
        newSequenceFlow.setTargetFlowElement(endFlowNode);

        // 7. 当前节点指向新方向
        List newSequenceFlowList = new ArrayList();
        newSequenceFlowList.add(newSequenceFlow);
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        // 8. 完成当前任务
        taskService.complete(task.getId());
    }

    /**
     * 根据当前用户的 审批人名称 即登录名称  查询对应审批人任务
     *
     * @param username
     * @return
     */
    private TaskQuery getTaskByUserName(String username) {
        // 并根据创建时间 做一个 降序排序
        return taskService.createTaskQuery()
                .taskAssignee(username)
                .orderByTaskCreateTime()
                .desc();
    }


    /**
     * 获取当前任务列表
     *
     * @param processInstanceId
     * @return
     */
    private List<Task> getCurrentTaskList(String processInstanceId) {
        List<Task> tasks = taskService
                .createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();
        return tasks;
    }
}
